//
//	(c) 2009 Andrew Kalmacky
//

#ifndef _AK_LTM_TEST_H_
#define _AK_LTM_TEST_H_

#include "ltm.h"
using ak::ltm::Object;
using ak::ltm::master;
using ak::ltm::pin;
using ak::ltm::weak;
using ak::ltm::cast;

#include "ltm_vector.h"

#include "master_vector.h"
using ak::ltm::master_vector;

#define CHECK(COND) log::info::assert((COND), STR(COND) T_(" ") STR(__FILE__) T_(":") STR(__LINE__))

namespace ltm_test
{
	using ak::ltm::vector;
	typedef Log::Logger<struct LtmTest, Log::lv_Verbose> log;

	struct Node: public ak::ltm::Object
	{
		master<Node> left, right;
		int i;
		Node(int i) : i(i) {}
		virtual void copy(Object*& dst) const { dst = new Node(*this); }
	};

	struct ComplexNode: Node
	{
		int v;
		weak<Node> wn;
		ComplexNode(int i, int v) : Node(i), v(v) {}
		virtual void copy(Object*& dst) const { dst = new ComplexNode(*this); }
	};

	pin<Node> make_node(int i, const pin<Node>& left, const pin<Node>& right)
	{
		pin<Node> r = new Node(i);
		r->left = left;
		r->right = right;
		return r;
	}
	void visit(const pin<Node>& n)
	{
		if (n)
		{
			visit(n->left);
			visit(n->right);
		}
	}

	struct LtmTest
	{
		LtmTest()
		{
			ak::ltm::copy_solver thread_solver;
			basic_test();
			sync_test();
			weak_test();
			ltm_vector_test();
			ltm_vector_test2();
			master_vector_test();
			log::info("ok");
		}
		void basic_test()
		{
			master<Node> root = new Node(5);
			master<Node> r2 = root;

			root->left = make_node(0x37, root, make_node(0x16, NULL, NULL));
			root->right = root->left;
			{
				pin<Node> t = root;
				while (t)
					t = t->left;
			}
			visit(root);

			root = new ComplexNode(1, 0);
			pin<ComplexNode> n = cast<ComplexNode>(root);
			n->v;
		}
		void sync_test()
		{
			master<Node, ak::ltm::sync> root = new Node(5);
			root->left = make_node(55, root, make_node(22, NULL, NULL));
			root->right = root->left;
			{
				pin<Node> t = root;
				while (t)
					t = t->left;
			}
			visit(root);

			root = new ComplexNode(1, 0);
			pin<ComplexNode> n = cast<ComplexNode>(root);
			n->v;
		}
		void weak_test()
		{
			master<Node> root = new Node(5);
			weak<Node> w = root;
			weak<Node> w2 = w;
			root.reset();
			Log::ltm::fatal::assert(!pin<Node>(w), T_("weak is not cleared"));
		}

		struct test_struct
		{
			int a, b;
		};

		void ltm_vector_test()
		{
			typedef vector<test_struct> vec_t;
			vec_t vec;
			for (int i = 0; i < 10; i++)
			{
				test_struct& s = vec.push_back();
				s.a = i;
				s.b = i * 10;
			}
			CHECK(vec.size() == 10);
			int n = 0;
			for (vec_t::iterator i = vec.begin(); i != vec.end(); ++i, ++n)
				CHECK(vec[n].a == n && i->b == n * 10);
			vec.pop_back();
			vec.pop_front();
			CHECK(vec.front().a == 1);
			CHECK(vec.back().a == 8);
			test_struct t={0, 0};
			vec.insert(vec.begin(), t);
			vec.move_items(vec.begin(), vec.begin() + 2, vec.end());
			for (int i = 0; i != vec.size(); ++i)
				CHECK(vec.at(i).a == (i + 2) % 9);
			vec.move_items(vec.begin() + 2, vec.begin(), vec.end() - 2);
			for (int i = 0; i != vec.size(); ++i)
				CHECK(vec[i].a == i);
			vec_t v2 = vec;
			vec.dispose();
			CHECK(vec.empty());
			CHECK(v2.size() == 9);
			for (int i = 0; i != v2.size(); ++i)
				CHECK(v2[i].a == i);
		}

		struct test_struct2
		{
			int a;
			master<Node> n;
			weak<Node> w;
			test_struct2()
			{
			}
			~test_struct2()
			{
			}
		};

		void ltm_vector_test2()
		{
			typedef vector<test_struct2> vec_t;
			vec_t vec;
			weak<Node> w;
			for (int i = 0; i < 10; i++)
			{
				test_struct2& s = vec.push_back();
				s.a = i;
				s.n = new Node(i);
				s.w = s.n;
			}
			CHECK(vec.size() == 10);
			w = vec[2].n;
			int n = 0;
			for (vec_t::iterator i = vec.begin(); i != vec.end(); ++i, ++n)
				CHECK(vec[n].a == n && i->n->i == n && pin<Node>(i->w) == pin<Node>(i->n));
			vec.pop_back();
			vec.pop_front();
			CHECK(vec.front().a == 1);
			CHECK(vec.back().a == 8);
			CHECK(pin<Node>(w) == pin<Node>(vec[1].n));
			test_struct2 t;
			t.a = 0;
			vec_t::iterator i = vec.insert(vec.begin(), t);
			i->n = new Node(0);
			i->w = vec[5].n;
			vec.move_items(vec.begin(), vec.begin() + 2, vec.end());
			for (int i = 0; i != vec.size(); ++i)
				CHECK(vec.at(i).a == (i + 2) % 9 && vec[i].n->i == (i + 2) % 9);
			vec.move_items(vec.begin() + 2, vec.begin(), vec.end() - 2);
			for (int i = 0; i != vec.size(); ++i)
				CHECK(vec[i].a == i);
			vec_t v2 = vec;
			vec.dispose();
			CHECK(vec.empty());
			CHECK(v2.size() == 9);
			for (int i = 0; i != v2.size(); ++i)
				CHECK(v2[i].a == i);
		}

		typedef master_vector<Node> nodes_t;
		void check_vector(nodes_t& nodes)
		{
			int num = 0;
			for (nodes_t::iterator i = nodes.begin(); i != nodes.end(); ++i, ++num)
				CHECK(cast<ComplexNode>(*i)->i == num && cast<ComplexNode>(*i)->v == num);
		}
		void master_vector_test()
		{
			nodes_t nodes;

			nodes.resize(10);
			CHECK(nodes.size() == 10);

			size_t num = 0;
			for (nodes_t::iterator i = nodes.begin(); i != nodes.end(); ++i)
				*i = new Node(num++);
			num = 0;
			for (nodes_t::iterator i = nodes.begin(); i != nodes.end(); ++i)
				CHECK((*i)->i == num++);

			for (size_t i = 0; i < nodes.size(); ++i)
				nodes[i] = new ComplexNode(int(i), int(i));

			check_vector(nodes);

			nodes_t new_nodes = nodes;
			check_vector(new_nodes);

			nodes.resize(0);
			CHECK(nodes.size() == 0);

			nodes.reserve(20);
			for (int i = 0; i < 25; ++i)
				nodes.push_back(new_nodes[i % new_nodes.size()]);


			nodes.pop_back();
			nodes.back()->i++;
			nodes.push_front(nodes.front());
			while (nodes.size())
				nodes.pop_front();
		}
	} test;

}
const char_t* const ltm_test::log::prefix = T_("[ltm_test]");

#undef CHECK

#endif // _AK_LTM_TEST_H_
